package com.apimanage.common.utils;

import com.apimanage.common.config.JwtProperties;
import com.apimanage.common.dto.UserDTO;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.crypto.SecretKey;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

/**
 * JWT令牌工具类
 * 用于生成、验证和解析JWT令牌
 * 该工具类设计为线程安全的，供所有服务模块共享使用
 * 采用静态方法设计模式，通过AtomicReference确保配置和密钥的线程安全访问
 *
 * @author API管理平台
 * @version 1.0
 * @since 2023-01-01
 */
public class JwtTokenUtil {

    private static final Logger logger = LoggerFactory.getLogger(JwtTokenUtil.class);

    // 使用AtomicReference实现线程安全的配置和密钥存储
    private static final AtomicReference<JwtProperties> JWT_PROPERTIES = new AtomicReference<>();
    private static final AtomicReference<SecretKey> SECRET_KEY = new AtomicReference<>();
    
    // JWT常量定义
    private static final int HS512_KEY_MIN_LENGTH_BYTES = 64; // HS512算法要求密钥至少64字节

    /**
     * 初始化JWT配置
     * 该方法应在应用启动时由Spring容器调用
     *
     * @param properties JWT配置属性
     */
    public static void initialize(JwtProperties properties) {
        if (JWT_PROPERTIES.compareAndSet(null, properties)) {
            // 配置未初始化时才进行初始化
            initializeSecretKey(properties.getSecret());
            logger.info("JWT工具类初始化成功");
        }
    }

    /**
     * 初始化JWT签名密钥
     * 遵循RFC 7518规范，HS512算法要求密钥长度至少为512位(64字节)
     * 
     * @param configSecret 配置文件中的密钥
     */
    private static void initializeSecretKey(String configSecret) {
        try {
            logger.debug("初始化JWT签名密钥");
            
            // 优先使用配置文件中的密钥，但对其进行标准化处理以确保长度足够
            if (configSecret != null && !configSecret.isEmpty()) {
                // 对配置密钥进行处理，确保生成的密钥长度符合HS512算法要求
                byte[] keyBytes = processSecretKey(configSecret);
                SecretKey secretKey = Keys.hmacShaKeyFor(keyBytes);
                int keySizeBits = secretKey.getEncoded().length * 8;
                
                if (SECRET_KEY.compareAndSet(null, secretKey)) {
                    logger.info("使用配置文件中的JWT密钥(已标准化处理)，密钥长度: {}位", keySizeBits);
                }
            } else {
                // 配置的密钥不存在，生成一个随机的密钥
                logger.warn("配置的JWT密钥不存在，将使用随机生成的密钥");
                if (SECRET_KEY.get() == null) {
                    SECRET_KEY.set(Keys.secretKeyFor(SignatureAlgorithm.HS512));
                    logger.info("JWT随机密钥生成成功");
                }
            }
        } catch (Exception e) {
            logger.error("JWT密钥初始化失败，将使用随机生成的密钥", e);
            if (SECRET_KEY.get() == null) {
                SECRET_KEY.set(Keys.secretKeyFor(SignatureAlgorithm.HS512));
            }
        }
    }

    /**
     * 处理配置的密钥字符串，确保其长度符合HS512算法要求
     * 
     * @param configSecret 配置文件中的密钥
     * @return 处理后的密钥字节数组
     */
    private static byte[] processSecretKey(String configSecret) {
        byte[] configBytes = configSecret.getBytes();
        
        // 如果配置密钥长度不足64字节，进行填充或拉伸
        if (configBytes.length < HS512_KEY_MIN_LENGTH_BYTES) {
            byte[] processedBytes = new byte[HS512_KEY_MIN_LENGTH_BYTES];
            
            // 使用简单的填充算法，基于配置密钥生成固定长度的密钥
            for (int i = 0; i < processedBytes.length; i++) {
                processedBytes[i] = configBytes[i % configBytes.length];
            }
            
            return processedBytes;
        }
        
        // 如果密钥长度超过要求，则截取
        return java.util.Arrays.copyOf(configBytes, HS512_KEY_MIN_LENGTH_BYTES);
    }

    /**
     * 获取JWT签名密钥
     *
     * @return SecretKey对象
     * @throws IllegalStateException 如果密钥未初始化
     */
    private static SecretKey getSecretKey() {
        SecretKey key = SECRET_KEY.get();
        if (key == null) {
            throw new IllegalStateException("JWT secret key is not initialized");
        }
        return key;
    }

    /**
     * 获取JWT配置（内部私有方法）
     * 
     * @return JWT配置对象
     * @throws IllegalStateException 如果配置未初始化
     */
    private static JwtProperties getJwtPropertiesInternal() {
        JwtProperties props = JWT_PROPERTIES.get();
        if (props == null) {
            throw new IllegalStateException("JWT properties are not initialized");
        }
        return props;
    }

    /**
     * 生成令牌
     *
     * @param userDTO 用户信息
     * @return JWT令牌
     */
    public static String generateToken(UserDTO userDTO) {
        try {
            logger.debug("开始生成JWT令牌，用户信息: username={}, id={}",
                    userDTO.getUsername(), userDTO.getId());

            Map<String, Object> claims = new HashMap<>();
            // 安全地处理userId可能为null的情况
            if (userDTO.getId() != null) {
                claims.put("userId", userDTO.getId());
                logger.debug("添加用户ID到JWT声明: {}", userDTO.getId());
            }
            claims.put("username", userDTO.getUsername());
            logger.debug("添加用户名到JWT声明: {}", userDTO.getUsername());

            // 处理角色信息
            if (userDTO.getRoles() != null && !userDTO.getRoles().isEmpty()) {
                claims.put("role", userDTO.getRoles().get(0));
                logger.debug("添加角色到JWT声明: {}", userDTO.getRoles().get(0));
                logger.debug("用户所有角色: {}", userDTO.getRoles());
            } else {
                logger.warn("用户没有分配角色: {}", userDTO.getUsername());
            }

            claims.put("created", new Date());
            logger.debug("添加创建时间到JWT声明");

            // 验证JWT配置 - 这里getJwtPropertiesInternal()会自动检查配置是否初始化
            getJwtPropertiesInternal();
            logger.debug("JWT配置验证成功");

            // 获取JWT签名密钥

            // 获取JWT签名密钥
            SecretKey secretKey = getSecretKey();

            // 生成过期时间
            Date expirationDate = generateExpirationDate();
            logger.debug("JWT过期时间: {}", expirationDate);

            // 构建JWT令牌
            logger.debug("开始构建JWT令牌");
            String token = Jwts.builder()
                    .setClaims(claims)
                    .setExpiration(expirationDate)
                    // 使用0.12.x版本兼容的签名方式
                    .signWith(secretKey, SignatureAlgorithm.HS512)
                    .compact();

            logger.debug("JWT令牌生成成功，令牌长度: {}", token != null ? token.length() : 0);
            return token;
        } catch (Exception e) {
            // 记录详细的异常信息，帮助排查问题
            logger.error("生成JWT令牌失败: 异常类型={}, 异常信息={}", e.getClass().getName(), e.getMessage());
            e.printStackTrace();
            throw new RuntimeException("Failed to generate JWT token", e);
        }
    }

    /**
     * 从令牌中获取用户名
     *
     * @param token JWT令牌
     * @return 用户名
     */
    public static String getUsernameFromToken(String token) {
        String username;
        try {
            Claims claims = getClaimsFromToken(token);
            username = claims.get("username").toString();
        } catch (Exception e) {
            username = null;
            logger.warn("从令牌中获取用户名失败", e);
        }
        return username;
    }

    /**
     * 从令牌中获取用户信息
     *
     * @param token JWT令牌
     * @return 用户信息
     */
    public static UserDTO getUserInfoFromToken(String token) {
        UserDTO userDTO = null;
        try {
            Claims claims = getClaimsFromToken(token);
            userDTO = new UserDTO();
            userDTO.setId(Long.parseLong(claims.get("userId").toString()));
            userDTO.setUsername(claims.get("username").toString());
            // 从JWT中获取角色并设置到roles列表中
            List<String> roles = new ArrayList<>();
            roles.add(claims.get("role").toString());
            userDTO.setRoles(roles);
        } catch (Exception e) {
            userDTO = null;
            logger.warn("从令牌中获取用户信息失败", e);
        }
        return userDTO;
    }

    /**
     * 获取令牌的过期时间
     *
     * @param token JWT令牌
     * @return 过期时间
     */
    public static Date getExpirationDateFromToken(String token) {
        Date expiration;
        try {
            Claims claims = getClaimsFromToken(token);
            expiration = claims.getExpiration();
        } catch (Exception e) {
            expiration = null;
            logger.warn("从令牌中获取过期时间失败", e);
        }
        return expiration;
    }

    /**
     * 验证令牌是否有效
     *
     * @param token JWT令牌
     * @return 是否有效
     */
    public static boolean validateToken(String token) {
        try {
            Claims claims = getClaimsFromToken(token);
            // 先检查claims是否为null，避免NullPointerException
            if (claims == null) {
                logger.warn("JWT令牌无效：无法解析令牌内容");
                return false;
            }
            
            Date expiration = claims.getExpiration();
            boolean isValid = expiration != null && expiration.after(new Date());
            logger.debug("JWT令牌验证结果: {}", isValid);
            return isValid;
        } catch (io.jsonwebtoken.security.SignatureException e) {
            // 特殊处理签名验证失败的情况，记录详细信息
            logger.warn("JWT签名验证失败：令牌可能被篡改或使用了错误的密钥", e);
            // 签名验证失败时返回false而不是抛出异常，保持与其他验证失败情况一致
            return false;
        } catch (Exception e) {
            logger.warn("JWT令牌验证失败", e);
            return false;
        }
    }

    /**
     * 刷新令牌
     *
     * @param token 旧令牌
     * @return 新令牌
     */
    public static String refreshToken(String token) {
        String refreshedToken;
        try {
            Claims claims = getClaimsFromToken(token);
            claims.put("created", new Date());
            // 获取JWT签名密钥
            SecretKey secretKey = getSecretKey();

            refreshedToken = Jwts.builder()
                    .setClaims(claims)
                    .setExpiration(generateExpirationDate())
                    // 使用0.12.x版本兼容的签名方式
                    .signWith(secretKey, SignatureAlgorithm.HS512)
                    .compact();
        } catch (Exception e) {
            refreshedToken = null;
            logger.warn("刷新JWT令牌失败", e);
        }
        return refreshedToken;
    }

    /**
     * 从令牌中获取Claims
     *
     * @param token JWT令牌
     * @return Claims
     */
    public static Claims getClaimsFromToken(String token) {
        Claims claims;
        try {
            // 获取JWT签名密钥
            SecretKey secretKey = getSecretKey();
            claims = Jwts.parser()
                    .setSigningKey(secretKey)
                    .build() // 在JWT 0.12.x版本中需要先构建JwtParser
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            claims = null;
            logger.warn("从令牌中获取Claims失败", e);
        }
        return claims;
    }

    /**
     * 生成过期时间
     *
     * @return 过期时间
     */
    private static Date generateExpirationDate() {
        return new Date(System.currentTimeMillis() + getJwtPropertiesInternal().getExpire());
    }

    /**
     * 获取当前使用的JWT配置
     *
     * @return JWT配置
     */
    /**
     * 获取当前使用的JWT配置
     *
     * @return JWT配置
     * @throws IllegalStateException 如果配置未初始化
     */
    public static JwtProperties getJwtProperties() {
        return getJwtPropertiesInternal();
    }
}